package top.blackr.folivora.common.pay.transfer;

import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.notification.NotificationConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.transferbatch.TransferBatchService;
import com.wechat.pay.java.service.transferbatch.model.InitiateBatchTransferRequest;
import com.wechat.pay.java.service.transferbatch.model.InitiateBatchTransferResponse;
import com.wechat.pay.java.service.transferbatch.model.TransferDetailInput;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import net.dreamlu.mica.core.utils.$;
import net.dreamlu.mica.core.utils.BeanUtil;
import net.dreamlu.mica.core.utils.JsonUtil;
import net.dreamlu.mica.core.utils.RandomType;
import org.springframework.beans.factory.annotation.Autowired;
import top.blackr.folivora.common.pay.exception.TransferException;
import top.blackr.folivora.common.pay.function.TransferCallbackConsumer;
import top.blackr.folivora.common.pay.function.TransferConsumer;
import top.blackr.folivora.common.pay.transfer.entity.wx.WxTransferRequest;
import top.blackr.folivora.common.pay.transfer.entity.wx.WxTransferResponse;
import top.blackr.folivora.config.ApplicationProperties;

import java.math.BigDecimal;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 微信提现默认实现，可直接继承该类
 *
 * @author BlackR
 */
@Getter
@Slf4j
public abstract class DefaultWxTransferStrategy implements ITransferStrategy<WxTransferRequest, WxTransferResponse, Map<String, Object>> {

    @Autowired
    private Config config;
    @Autowired
    private ApplicationProperties.WxPay wxProperties;
    @Autowired
    private NotificationConfig notificationConfig;

    @Override
    public void generateNo(WxTransferRequest request) {
        if ($.isEmpty(request.getOutBatchNo())) {
            request.setOutBatchNo($.random(9, RandomType.ALL));
        }
    }

    @Override
    public void transfer(WxTransferRequest transferRequest, TransferConsumer<WxTransferResponse> consumer) {
        // 交易订单号
        generateNo(transferRequest);
        WxTransferRequest.TransferDetail transferDetail = transferRequest.getTransferDetail();
        // 明细转账金额<0.3元时，不允许填写收款用户姓名
        if (transferDetail.getTransferAmount().compareTo(new BigDecimal("0.03")) < 0) {
            transferDetail.setUserName(null);
        }
        // 明细转账金额 >= 2,000元时，该笔明细必须填写收款用户姓名
        if (transferDetail.getTransferAmount().compareTo(new BigDecimal("2000")) >= 0
                && $.isEmpty(transferDetail)) {
            throw new TransferException("转账金额 >= 2,000元时，该笔明细必须填写收款用户姓名");
        }

        Long transferAmount = transferDetail.getTransferAmount().multiply(new BigDecimal("100")).longValue();
        InitiateBatchTransferResponse transferResponse;
        try {
            TransferBatchService service = new TransferBatchService.Builder().config(config).build();

            InitiateBatchTransferRequest batchTransferRequest = new InitiateBatchTransferRequest();
            batchTransferRequest.setAppid(wxProperties.getAppId());
            // 商家批次单号
            batchTransferRequest.setOutBatchNo(transferRequest.getOutBatchNo());
            // 批次名称
            batchTransferRequest.setBatchName(transferRequest.getBatchName());
            // 批次备注
            batchTransferRequest.setBatchRemark(transferRequest.getBatchRemark());
            // 转账总金额单位：分
            batchTransferRequest.setTotalAmount(transferAmount);
            // 转账总笔数
            batchTransferRequest.setTotalNum(1);
            // 转账明细列表
            TransferDetailInput detailInput = new TransferDetailInput();
            BeanUtil.copy(transferDetail, detailInput);
            detailInput.setTransferAmount(transferAmount);
            List<TransferDetailInput> transferDetailInputs = Collections.singletonList(detailInput);
            batchTransferRequest.setTransferDetailList(transferDetailInputs);
            // 通知地址
            batchTransferRequest.setNotifyUrl(transferRequest.getNotifyUrl());
            transferResponse = service.initiateBatchTransfer(batchTransferRequest);
            log.info("微信提现申请【{}】状态：【{}】", transferResponse.getOutBatchNo(), transferResponse.getBatchStatus());
            // 转账后续
            consumer.accept(BeanUtil.copy(transferResponse, WxTransferResponse.class));
        } catch (Exception e) {
            log.error("微信提现申请失败：{}", e.getMessage());
            throw e;
        }
    }

    @Override
    public Map<String, String> transferCallback(String requestBody, Map<String, String> headers, TransferCallbackConsumer<Map<String, Object>> callbackConsumer) {
        Map<String, String> map = new HashMap<>(4);
        try {
            // 构造 RequestParam
            RequestParam requestParam = new RequestParam.Builder()
                    .serialNumber(headers.get("Wechatpay-Serial"))
                    .nonce(headers.get("Wechatpay-Nonce"))
                    .signature(headers.get("Wechatpay-Signature"))
                    .timestamp(headers.get("Wechatpay-Timestamp"))
                    .body(requestBody)
                    .build();
            if (requestParam.getSignature().contains("WECHATPAY/SIGNTEST")) {
                throw new TransferException("微信回调探测流量");
            }

            // 验签
            NotificationParser parser = new NotificationParser(notificationConfig);
            Transaction transaction = parser.parse(requestParam, Transaction.class);
            log.info("微信提现回调通知：{}", JsonUtil.toJson(transaction));
            // 此处处理业务
            callbackConsumer.accept(BeanUtil.toMap(transaction));
            // 返回回调处理结果
            map.put("code", "SUCCESS");
            map.put("message", "成功");
            return map;
        } catch (TransferException e) {
            map.put("code", "FAIL");
            map.put("message", "失败");
            return map;
        } catch (Exception e) {
            log.error("微信支付回调失败-商品:", e);
            throw e;
        }
    }
}
